package rs.udd.service;

import java.io.File;
import java.io.FileInputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.pdfbox.pdfparser.PDFParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import rs.udd.constants.GeneralConstants;
import rs.udd.constants.UddMessages;
import rs.udd.exceptions.MissingMetaDataException;
import rs.udd.exceptions.UddException;
import rs.udd.exceptions.UserAuthenticationCheckException;
import rs.udd.model.Admin;
import rs.udd.model.Advert;
import rs.udd.model.Advertiser;
import rs.udd.model.Category;
import rs.udd.model.KeyWord;
import rs.udd.repository.AdvertRepository;
import rs.udd.repository.AdvertiserRepository;
import rs.udd.repository.CategoryRepository;
import rs.udd.repository.KeyWordRepository;
import rs.udd.service.document.indexer.IndexManager;
import rs.udd.service.document.indexer.UDDIndexer;
import rs.udd.service.document.indexer.handler.PDFHandler;
import rs.udd.service.dto.advert.AdvertDto;
import rs.udd.service.dto.advertiser.AdvertiserEditDto;
import rs.udd.service.dto.advertiser.AdvertiserRegisterDto;
import rs.udd.service.dto.user.UserPasswordEditDto;
import rs.udd.service.mappings.AdvertiserMapping;
import rs.udd.service.util.FileUtil;
import rs.udd.service.util.FileUtil.FileTypes;
import rs.udd.service.util.SecurityUtil;

@Service
@Transactional
public class AdvertiserServiceImpl implements AdvertiserService {

	private static final Logger LOGGER = LogManager.getLogger(AdvertiserServiceImpl.class);

	@Resource
	private AdvertiserRepository advertiserRepository;

	@Resource
	private AdvertRepository advertRepository;

	@Resource
	private CategoryRepository categoryRepository;
	
	@Resource
	private KeyWordRepository keyWordRepository;

	@Autowired
	private PasswordEncoder encoder;

	@Autowired
	private MailService mailService;

	@Autowired
	private LinkService linkService;

	@Override
	public String register(AdvertiserRegisterDto dto) throws UddException {
		try {
			LOGGER.info("Advertiser registering...");

			String encodedPassword = encoder.encode(dto.getPassword());
			dto.setPassword(encodedPassword);

			Advertiser advertiser = AdvertiserMapping.register(dto);

			String returnMessage = "";
			boolean adminLoggedIn = SecurityUtil.checkLoggedRole(Admin.class);
			if (adminLoggedIn) {	// admin can register advertiser without any additional requirements or conditions
				returnMessage = "";
				advertiser.setEmailActive(true);
				advertiser.setActive(true);
			}
			LOGGER.info("Advertiser registered successfully.");
			advertiser = advertiserRepository.save(advertiser);

			if (!adminLoggedIn) {
				String message = UddMessages.MESSAGE_EMAIL_GREETING.replace("[username]", advertiser.getUsername()) + "\n\n" + UddMessages.MESSAGE_EMAIL_ACTIVATION_TEXT + "\n\n\n" + UddMessages.MESSAGE_EMAIL_VALEDICTION;
				String link = GeneralConstants.APP_LINK + "link/advertiserRegistration?random=" + linkService.generateAndSaveAdvertiserLink(advertiser, advertiser.getEmail());
				message = message.replace("[link]", link);

				mailService.sendMail(advertiser.getEmail(), UddMessages.MESSAGE_EMAIL_REGISTRATION_SUBJECT, message);
				returnMessage = UddMessages.INFO_ADVERTISER_SUCCESSFULLY_REGISTERED;
			}

			return returnMessage;
		} catch (Exception e) {
			throw new UddException("Udd exception", e);
		}
	}

	@Override
	public Advertiser save(Advertiser advertiser) throws UddException {

		try {
			return advertiserRepository.save(advertiser);
		} catch (Exception e) {
			throw new UddException("Udd exception", e);
		}
	}

	@Override
	public Advertiser edit(AdvertiserEditDto dto) throws UddException {
		try {
			Advertiser advertiser = advertiserRepository.findOne(dto.getId());
			AdvertiserMapping.edit(dto, advertiser);

			return advertiserRepository.save(advertiser);
		} catch (Exception e) {
			throw new UddException("Udd exception", e);
		}
	}

	@Override
	public void delete(Long id) throws UddException {
		try {

			Advertiser advertiser = advertiserRepository.findOne(id);

			for (Advert advert : advertiser.getAdverts()) {
				deleteAdvert(advert);
			}			

			advertiserRepository.delete(id);
		} catch (Exception e) {
			throw new UddException("Udd exception", e);
		}
	}

	@Override
	public List<Advertiser> findAll() throws UddException {
		try {
			return advertiserRepository.findAll();
		} catch (Exception e) {
			throw new UddException("Udd exception", e);
		}
	}

	@Override
	public boolean editPassword(UserPasswordEditDto dto) throws UddException {

		LOGGER.info("AdvertiserServiceImpl.editPassword()");

		try {

			Advertiser checkingUser = advertiserRepository.findOne(dto.getId());
			SecurityUtil.checkLoggedUser(checkingUser);
			SecurityUtil.checkPasswordChange(dto);

			String oldRawPassword = dto.getOldPassword();
			String oldEncodedPassword = checkingUser.getPassword();
			boolean result = encoder.matches(oldRawPassword, oldEncodedPassword);

			if (result) {
				String newRawPassword = dto.getNewPassword();
				String newEncodedPassword = encoder.encode(newRawPassword);
				checkingUser.setPassword(newEncodedPassword);
				advertiserRepository.save(checkingUser);

				String to = checkingUser.getEmail();
				String body = "Your password has been changed. New password is: " + newRawPassword;
				mailService.sendMail(to, "Password change", body);
			}

			return result;

		} catch (Exception e) {

			if (e instanceof UserAuthenticationCheckException) {
				throw e;
			}

			throw new UddException("Udd exception", e);
		}

	}

	@Override
	public AdvertDto uploadAdvert(AdvertDto advertDto, File file) throws MissingMetaDataException, UddException {		
		try {
			PDFParser parser = new PDFParser(new FileInputStream(file));
			IndexManager.getIndexer().parseAndValidate(parser, advertDto);

			if (advertDto.isNotEmpty()) {
				Advertiser advertiser = getLoggedAdvertiser();

				String indexedDocumentId = UDDIndexer.generateDocumentId();
				IndexManager.getIndexer().index(file, advertDto, indexedDocumentId);

				Advert advert = new Advert();

				advert.setIndexedDocumentId(indexedDocumentId);
				advert.setFilePath(FileUtil.getFolderPathForAdvert(advertiser.getUsername(), FileTypes.ADVERT) + file.getName());
//				advert.setTitle(advertDto.getMetaTitle());
				advert.setAbstractText(advertDto.getAbstractText());
				advert.setTitle(advertDto.getTitle());
				
				advert.setAdvertiser(advertiser);
				advertiser.getAdverts().add(advert);

				Set<KeyWord> keywords = new HashSet<KeyWord>();
				for (String str : advertDto.getMetaKeywords().split(PDFHandler.META_DATA_SEPARATOR)) {
					String word = str.trim();
					KeyWord keyWord = keyWordRepository.findByWord(word);
					if (keyWord == null) {
						keyWord = new KeyWord();
						keyWord.setWord(word);
					}

					keyWord.getAdverts().add(advert);
					keywords.add(keyWord);
				}
				advert.setKeyWords(keywords);

				//TODO: add metaAuthor ???

				String categoryNames[] = advertDto.getCategoryNames().trim().split(PDFHandler.META_DATA_SEPARATOR);
				Set<Category> categories = parseCategories(categoryNames);
				for (Category category : categories) {
					category.getAdverts().add(advert);
				}
				advert.setCategories(categories);
				
				advertRepository.save(advert);
			}
			return advertDto;
		} catch (Exception e) {
			throw new UddException(e.getMessage(), e);
		}

	}

	private Set<Category> parseCategories(String categoryNames[]) {
		Set<Category> categories = new HashSet<Category>();
		for (String categoryName : categoryNames) {
			categoryName = categoryName.trim();
			Category category = categoryRepository.findCategoryByName(categoryName);
			categories.add(category);
		}

		return categories;
	}

	@Override
	public boolean uploadAdvertAgain(File file, AdvertDto advertDto) throws UddException {

		try {
			Advertiser advertiser = getLoggedAdvertiser();

			//			String indexedDocumentId = Long.toString(System.currentTimeMillis());
			String indexedDocumentId = UDDIndexer.generateDocumentId();
			IndexManager.getIndexer().index(file, advertDto, indexedDocumentId);

			Advert advert = new Advert();
			advert.setIndexedDocumentId(indexedDocumentId);

			advert.setFilePath(FileUtil.getFolderPathForAdvert(advertiser.getUsername(), FileTypes.ADVERT) + file.getName());
			advert.setTitle(advertDto.getMetaTitle());
			advert.setAbstractText(advertDto.getAbstractText());
			advert.setAdvertiser(advertiser);
			advertiser.getAdverts().add(advert);
			
			Set<KeyWord> keywords = new HashSet<KeyWord>();
			for (String str : advertDto.getMetaKeywords().split("\r")) {
				for (String word : str.split(" ")) {
					word = word.trim();
					KeyWord keyWord = keyWordRepository.findByWord(word);
					if (keyWord == null) {
						keyWord = new KeyWord();
						keyWord.setWord(word);
					}
	
					keyWord.getAdverts().add(advert);
					keywords.add(keyWord);
				}
			}

			//TODO: maybe add metaAuthor ???

			advert.setKeyWords(keywords);

			String categoryNames[] = advertDto.getCategoryNames().trim().split(PDFHandler.META_DATA_SEPARATOR);
			Set<Category> categories = parseCategories(categoryNames);
			for (Category category : categories) {
				category.getAdverts().add(advert);
			}
			advert.setCategories(categories);
			advert.setTitle(advertDto.getTitle());

			advertRepository.save(advert);

			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}

		return false;
	}

	// Creating the directory to store file
	// String rootPath = System.getProperty("catalina.home");
	// File dir = new File(rootPath + File.separator + "tmpFiles");
	// if (!dir.exists())
	// dir.mkdirs();

	@Override
	public Advertiser findOne(Long id) throws UddException {
		try {
			return advertiserRepository.findOne(id);
		} catch (Exception e) {
			throw new UddException("Udd exception", e);
		}
	}

	@Override
	public Advertiser getLoggedAdvertiser() {
		Long advertiserId = ((Advertiser)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId();

		Advertiser advertiser = advertiserRepository.findOne(advertiserId);

		advertiser.getAdverts().size();

		return advertiser;
	}

	public void deleteAdvert(Advert advert) throws UddException {
		try {
			String indexedId = advert.getIndexedDocumentId();

			boolean deletionResult = false;
			if (StringUtils.isNotEmpty(indexedId)) {

				List<Document> allDocuments = IndexManager.getIndexer().getAllDocuments();
				for (Document doc : allDocuments) {
					String documentId = doc.get(PDFHandler.ID);
					if (documentId.equals(indexedId)) {
						deletionResult = IndexManager.getIndexer().deleteDocument(doc);
						break;
					}
				}
			}
			LOGGER.info("Advert index deleted: " + deletionResult);	

			String folderPath = FileUtil.getFolderPathForAdvert(advert.getAdvertiser().getUsername(), FileTypes.ADVERT);
			File dir = new File(folderPath);
			if (dir.exists()) {
				FileUtils.deleteDirectory(dir);
			}

			advertRepository.delete(advert.getId());
			LOGGER.info("Advert deleted from DB.");
		} catch (Exception e) {
			throw new UddException("Udd exception", e);
		}
	}

	@Override
	public Advertiser findByIdForView(Long id) {
		Advertiser advertiser = advertiserRepository.findOne(id);
		advertiser.getAdverts().size();
		advertiser.getHiddenByAgents().size();
		advertiser.getContactedAgents().size();
		
		return advertiser;
	}

}
